package br.ufpe.cin.mac.middleware;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;

import br.ufpe.cin.mac.middleware.connectors.concrete.TcpClient;
import br.ufpe.cin.mac.middleware.connectors.concrete.UDPClient;
import br.ufpe.cin.mac.middleware.exceptions.InvokeException;
import br.ufpe.cin.mac.util.Constants;
import br.ufpe.cin.mac.util.Loader;
import br.ufpe.cin.mac.util.Message;

/**
 * Class Middleware
 * 
 * @author Osmany
 * @author Rafael Roque
 * 
 */
public class Middleware {

	private MBeanServer mbServer;

	private ObjectName configurator;

	private Map<String, Object> apps;

	private List<String> buffer;

	public Middleware(String host, String confFile) {
		this.mbServer = MBeanServerFactory.createMBeanServer();
		this.mbServer = Loader.loader(this.mbServer, confFile);
		buffer = Collections.synchronizedList(new LinkedList<String>());

		try {
			this.configurator = new ObjectName(this
					.getObjectName("Configurator"));
			final ObjectName udp,tcp;

			if ("server".equals(host)) {
				tcp = new ObjectName(getObjectName("TcpServer"));
				udp = new ObjectName(getObjectName("UDPServer"));

				new Thread() {
					public void run() {
						try {
							mbServer.invoke(udp, "connect", null, null);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}.start();
				new Thread() {
					public void run() {
						try {
							mbServer.invoke(tcp, "connect", null, null);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}.start();
			} else {				
				// http = new ObjectName(getObjectName("HttpClient"));
				
				//udp = new ObjectName(getObjectName("UDPClient"));		
				
				String[] split = host.split(":");
				String server = split[0];
				int port = Integer.parseInt(split[1]);
				UDPClient.port = Constants.UDP_PORT;
				UDPClient.server = server;
				TcpClient.host = server;
				TcpClient.port = Constants.TCP_PORT;
				//UDPClient client = UDPClient.reference;				
				//String[] sig = new String[] { String.class.getName() };
				//Object[] param = new Object[] { host };
				// Acessa a porta e obt�m a mensagem
				//Method meth = UDPClient.class.getMethod("setHost", new Class<?>[]{ String.class});
				//mbServer.invoke(udp, meth.getName(), param, sig);

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void addApp(Object app, String name) {
		if (!apps.containsKey(name)) {
			apps.put(name, app);
		}
	}

	/**
	 * 
	 * @param msg
	 *            possui todos os valores necessarios para o invoke do MBean do
	 *            servidor remoto. A msg so enviada ao manager, l� �
	 *            desencapsulada, seus atributos sao lidos e o invoke eh feito
	 */
	public void send(Message msg, String type) throws InvokeException {
		if (msg != null) {
			Object[] data = { msg };
			String[] sig = { Message.class.getName() };

			try {
				this.mbServer.invoke(this.configurator, "invC", data, sig);
			} catch (Exception e) {
				throw new InvokeException("Middleware->Send", e);
			}
		} else
			System.err.println("Middleware.send ERROR!!");
	}

	/**
	 * Esse metodo retorna todas as mensagens, para uma dada, aplicacao
	 * existente no buffer do middleware
	 * 
	 * @param application
	 * @return
	 */
	public List<Message> receive(String application) {

		LinkedList<Message> ret = null;
		try {
			Object[] data = new Object[] { application };
			String[] sig = new String[] { String.class.getName() };

			ret = (LinkedList<Message>) this.mbServer.invoke(this.configurator,
					"readMessages", data, sig);

		} catch (Exception e) {
			e.printStackTrace();
			return ret;
		}
		return ret;
	}

	public List<Message> getBuffer() {

		List<Message> ret = new LinkedList<Message>();
		try {
			Object[] data = new Object[] { "Teste" };
			String[] sig = new String[] { String.class.getName() };
			Collection<Message> buffer = (Collection<Message>) this.mbServer
					.invoke(this.configurator, "getBuffer", data, sig);

			Iterator<Message> it = buffer.iterator();

			while (it.hasNext()) {
				ret.add(it.next());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * Realiza busca nos MBeans registrados no MBeanServer segundo expressoes
	 * regulares passadas como parametro
	 * 
	 * @param obj
	 * @return
	 */
	private String getObjectName(String obj) {
		try {
			ObjectName scope = new ObjectName("*:*,name=" + obj);
			Set<ObjectName> results = this.mbServer.queryNames(scope, null);
			Iterator<ObjectName> i = results.iterator();

			if (i.hasNext()) {
				ObjectName objName = i.next();
				return objName.getCanonicalName();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
